﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;

namespace OpenLS.Spreadsheet
{
    internal static class XmlStringHelper
    {
        private static readonly Regex encodeRegex = new Regex(GetDecodeRegexValue(),
                                                              RegexOptions.Compiled | RegexOptions.Singleline);

        private static readonly Regex decodeRegex = new Regex("(_x005F_x[0-9]{4}_)|" + simpleCode,
                                                              RegexOptions.Compiled | RegexOptions.Singleline);

        public static string Encode(string value)
        {
            Contract.Requires(value != null);
            Contract.Ensures(Decode(Contract.Result<string>()) == value);
            MatchCollection matches = encodeRegex.Matches(value);
            if (matches.Count == 0)
                return value;
            int index = 0;
            var result = new StringBuilder();
            foreach (Match match in matches)
            {
                if (match.Index > index)
                {
                    result.Append(value.Substring(index, match.Index - index));
                }
                string s = match.Value;
                if (s.Length == 1)
                {
                    result.Append(string.Format("_x{0:X4}_", (int) s[0]));
                }
                else
                {
                    result.Append("_x005F" + s);
                }

                index = match.Index + match.Length;
            }
            if (index < value.Length)
                result.Append(value.Substring(index, value.Length - index));
            return result.ToString();
        }

        private static string GetDecodeRegexValue()
        {
            var result = new StringBuilder(simpleCode);
            foreach (int specialCode in GetSpecialCodes())
            {
                result.AppendFormat("|(\\u{0})", specialCode.ToString("X4"));
            }
            return result.ToString();
        }

        private const string simpleCode = "(_x[0-9a-fA-F]{4}_)";

        public static string Decode(string value)
        {
            Contract.Requires(value != null);
            MatchCollection matches = decodeRegex.Matches(value);
            if (matches.Count == 0)
                return value;

            int index = 0;
            var result = new StringBuilder();
            foreach (Match match in matches)
            {
                if (match.Index > index)
                    result.Append(value.Substring(index, match.Index - index));
                switch (match.Value.Length)
                {
                    case 7:
                string s = match.Value.Replace("_", "").Replace("x", "");
                        int i = int.Parse(s,NumberStyles.HexNumber);
                        result.Append((char)i);
                        break;
                    case 13:
                        result.Append(match.Value.Substring(6, 7));
                        break;
                    default: throw new NotSupportedException();
                }
                index = match.Index + match.Length;
            }
            if (index < value.Length)
                result.Append(value.Substring(index, value.Length - index));
            return result.ToString();
        }

        private static IEnumerable<int> GetSpecialCodes()
        {
            yield return 0x0001;
            yield return 0x0002;
            yield return 0x0003;
            yield return 0x0004;
            yield return 0x0005;
            yield return 0x0006;
            yield return 0x0007;
            yield return 0x0008;
            yield return 0x000B;
            yield return 0x000C;
            yield return 0x000D;
            yield return 0x000E;
            yield return 0x000F;
            yield return 0x0010;
            yield return 0x0011;
            yield return 0x0012;
            yield return 0x0013;
            yield return 0x0014;
            yield return 0x0015;
            yield return 0x0016;
            yield return 0x0017;
            yield return 0x0018;
            yield return 0x0019;
            yield return 0x001A;
            yield return 0x001B;
            yield return 0x001C;
            yield return 0x001D;
            yield return 0x001E;
            yield return 0x001F;
        }
    }
}